When a new device for a computer or other electronic system is being developed, a software model of the device (also referred to herein as a “device model,” “device under test,” or “DUT”) is often created in order to test the device's functionality before implementing the device in hardware. The device model is then thoroughly tested to try to identify design problems. This enables device designers to modify the design, if necessary, to increase the likelihood that the device will operate in a consistent and predictable manner.
For example, new designs for memory or system controllers are typically tested using a device model. In order to test such a software model, a “test command generation program” first creates a sequence of read and write commands, and stores these commands in a text file. A device model test program then executes those commands.
At a minimum, a “write” command includes a location (e.g., a memory address) and one or more bits or bytes of data that are to be written to that location. A “read” command is used to verify that data was properly written to a location, and that the data can be accurately retrieved.
In generating each write or read command within a command list, it is common to randomly select the write locations in order to improve the robustness of the test. Therefore, a series of write operations may jump randomly to various locations, rather than sequentially writing to consecutive locations. The location selected for a read command is constrained by the fact that it is necessary for a data location to be written to before it is read from. Otherwise, valid data would not exist at the location.
Table 1, below, is a simplified example of a sequence of write/read commands, which could be generated by a test command generation program and later executed by a device model test program. Each command corresponds to a write to or a read from a single location of a device. Each entry includes the location corresponding to the operation and a byte of data that is to be written or that is expected to exist at the location.
TABLE 11.write (addr => x“4005FAC0”, data_value => x“A1”)2.write (addr => x“001FE600”, data_value => x“07”)3.read (addr => x“4005FAC0”, data_value => x“A1”)4.write (addr => x“40331A18”, data_value => x“00”)5.read (addr => x“001FE600”, data_value => x“07”)6.read (addr => x“40331A18”, data_value => x“00”)7.write (addr => x“001FF230”, data_value => x“FF”)8.read (addr => x“001FF230”, data_value => x“FF”)
The example commands, above, could be used to test a DUT, such as a memory controller, that writes to and reads from a single device (e.g., a dynamic random access memory (DRAM)). In contrast to a simple memory controller, a system controller or other device could write to or read from multiple devices, such as one or more memory devices and/or input/output (I/O) devices, for example. Therefore, write and read commands for system controllers or other devices often include an additional field, not shown in the example commands above, which indicates what device is being written to or read from. In addition, a command could indicate that multiple locations should be written to or read from. For ease of illustration, however, the above commands apply to one-byte data transfers to or from a single, unspecified device.
FIG. 3 illustrates a flowchart of a method for generating a command list in accordance with the prior art. The method for generating the command list begins, in block 302, by initializing an array, which will store a history or transaction trace table. The history table is used to maintain a record of the address and data value for each generated write command, thereby capturing the expected state of a DUT that will execute the instructions. The history table is also used to generate subsequent read commands, as will be explained in more detail below.
To initialize the array, the prior art method allocates a block of memory sufficient to hold a distinct array entry for each write operation that the test ultimately will perform. Therefore, for example, if the user indicated that he or she wanted the test to perform 1000 write operations, then the method would allocate memory to store at least 1000 entries.
Next, in block 304, a write pointer and a read pointer are initialized to a value of 0. The write pointer is used to indicate which entry, within the array, the next location and data value that will correspond to a write command will be stored. The read pointer is used to indicate the entry, in the array, where the method should look to get a location and a data value for the next read command. Because no commands have been created, both pointers are initially set to 0.
Next, in block 306, a command type is chosen. The command type is either a “write” or a “read.” The first command is constrained to select a “write” type command, so that valid data will exist within a location before that location is read from.
A determination is then made, in block 308, whether the command is a “write” or a “read.” If the command is a “write,” then a location within the range of defined locations being tested is randomly chosen, in block 310. In some cases, a write size also can be chosen, which indicates a number of locations of data to write. For ease of illustration, commands for writing and reading only a single location are described in the context of FIG. 3.
A determination is then made, in block 312, whether or not a command to write to that location has been previously generated. This can be done by evaluating the entries previously stored within the array. If so, then an additional determination is made whether a command has been generated to read the data at that location, in block 314. This can be done by determining whether the read pointer points to an array entry that occurs after the array entry that includes the location. If a command to verify (i.e., read) the data has not been generated, then another location is chosen, in block 310, and the method iterates.
If a command to verify the data has been generated (as determined in block 314), or if a command to write to the location was not previously generated (as determined in block 312), then the command parameters (e.g., location, data size, data) are written in the entry of the array that is indicated by the write pointer, in block 316. The write pointer is then incremented, in block 318, to point to the next entry. In block 320, a write command is then generated, by creating a new command in the command list (e.g., Table 1).
Referring back to block 308, if a determination is made that the selected command type is a “read” command, then a determination is made, in block 322, whether or not the write pointer is greater than the read pointer. If not, then a read command cannot be generated, and the procedure returns to block 306 to choose a command type. If so, then the command parameters are retrieved from the array entry at which the read pointer is pointing, in block 324. The read pointer is then incremented, in block 326, and a read command is generated, in block 320, by creating a new command in the command list (e.g., Table 1).
After block 320, a determination is made whether the method is done generating test commands, in block 328. Typically, a user has pre-specified the number of commands that the user wants the program to generate. If the number of pre-specified commands has been generated, then a determination is made that the method is done. Otherwise, the method iterates as shown until the pre-specified number of commands has been generated.
Prior art methods for generating a command list have several limitations. First, since the read pointer sequentially steps through the array entries, each read command must correspond, in exact sequence, to the write commands that were previously generated. Second, if a particular location has already been written to, a second write command to that location cannot be generated until the data previously stored in that location has been verified completely with one or more read commands. Otherwise, the verify phase would see different data from what is expected, and an error would be declared.
Because of these and other limitations discussed elsewhere, the tests run using the prior art methods of command generation are inherently inflexible, and often are not particularly robust or concise. For example, in a more robust test, it may be desirable to write to a location multiple times before the location is read from. In addition, a more robust test should be able to perform reads to various locations in a substantially different order than writes to those locations. Also, it may be desired to read more or fewer locations than were written to in previously generated write commands.
Another disadvantage to prior art methods is that the array used in the command generation process can be extremely large, consuming large amounts of memory and possibly limiting the number of commands that may be generated. This is particularly true in cases where it is desired to write to and verify locations multiple times. For example, if a user wants every address in memory to be written to two or more times, then the array would have to have at least twice as many entries as there are address locations. For tests corresponding to large memory devices, this means that the array could consume large quantities of memory.
Such prior art methods for generating a command list, such as the list illustrated above, have several limitations. First, each read command must correspond, in exact sequence, to write commands that were previously generated. Thus, as illustrated in the example above, the first read command (i.e., command #3) can only read from the location specified in the first write command (i.e., command #1). Similarly, the second read command (i.e., command #5) can only read from the location specified in the second write command (i.e., command #2), and so on. Second, if a particular location has already been written to, a second write command to that location cannot be generated until the data previously stored in that location has been verified with a read command. Otherwise the read command might see different data from what is expected, and an error would be declared.
Because of these and other limitations, the tests run using the prior art methods of command generation are inflexible and often are very long. For example, prior art methods cannot write to a location multiple times before reading from the location. In addition, prior art methods cannot perform reads of various locations in a different order from the order of writes to those locations. Nor can prior art methods perform a read operation that has a size that is different from the size of the corresponding write operation. Instead, a read operation must completely verify a corresponding write operation, using prior art methods, and is unable to “sub-verify” a write operation.
Using prior art methods, all test commands are sequentially included in an array while the commands are being generated. Accordingly, another disadvantage to prior art methods is that the amount of memory used in the command generation process can be extremely large. This is particularly true in cases where it is desired to write to and verify locations multiple times. For example, if a user wants every location to be written to two or more times, then the array would have to include at least twice as many entries as there are locations. For tests corresponding to large address ranges, this means that the array would consume large quantities of memory during the test generation process.
Accordingly, what is needed is a test command generation method that is more flexible and concise. Specifically needed is a test command generation method that allows read commands to be generated in a different sequence from previously generated write commands. Further needed is a test command generation method that allows multiple write commands for a particular location to be generated, regardless of whether a read command to verify the data has been generated. In addition, what is needed is a test command generation method that enables read commands to be more varied and flexible (e.g., allowing read commands that specify different data sizes and different masks than the data sizes and masks specified in previously generated write commands). Moreover, in connection with variable and flexible read commands, a test command generation method is needed that maximizes the amount of data that is newly verified and minimizes the amount of unnecessary reads (re-verification). Also needed is a method that enables more test permutations to be performed for better test coverage, without requiring unreasonably lengthy command lists. Further needed is a test command generation method that requires less overhead (e.g., less memory) to execute.